// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package mime

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"strings"
	"unicode"
	"unicode/utf8"
)

// 字编码器是RFC 2047编码字编码器。
type WordEncoder byte

const (
	// BEncoding表示RFC 2045定义的Base64编码方案。
	BEncoding = WordEncoder('b')
	// QEncoding表示RFC 2047定义的Q编码方案。
	QEncoding = WordEncoder('q')
)

var (
	errInvalidWord = errors.New("mime: invalid RFC 2047 encoded-word")
)

// Encode返回s的编码字形式。如果s是ASCII，没有特殊的
// 字符，则返回时保持不变。提供的字符集是s的IANA 
// 字符集名称。它不区分大小写。
func (e WordEncoder) Encode(charset, s string) string {
	if !needsEncoding(s) {
		return s
	}
	return e.encodeWord(charset, s)
}

func needsEncoding(s string) bool {
	for _, b := range s {
		if (b < ' ' || b > '~') && b != '\t' {
			return true
		}
	}
	return false
}

// encodeWord将字符串编码为编码字。
func (e WordEncoder) encodeWord(charset, s string) string {
	var buf strings.Builder
	// 可以使用类似len（s）*3的提示，但这对于单词拆分的情况是不够的，对于简单的输入来说太多了。
	// 48接近maxEncodedWordLen/2，但已调整为分配器大小类。
	buf.Grow(48)

	e.openWord(&buf, charset)
	if e == BEncoding {
		e.bEncode(&buf, charset, s)
	} else {
		e.qEncode(&buf, charset, s)
	}
	closeWord(&buf)

	return buf.String()
}

const (
	// 编码字的最大长度为75个字符。
	// 参见RFC 2047第2节。
	maxEncodedWordLen = 75
	// maxContentLen是可以编码多少内容，忽略页眉和
	// 2字节页脚。
	maxContentLen = maxEncodedWordLen - len("=?UTF-8?q?") - len("?=")
)

var maxBase64Len = base64.StdEncoding.DecodedLen(maxContentLen)

// bEncode使用base64编码进行编码，并将其写入buf。
func (e WordEncoder) bEncode(buf *strings.Builder, charset, s string) {
	w := base64.NewEncoder(base64.StdEncoding, buf)
	// 如果字符集不是UTF-8或内容很短，请不要麻烦
	// 拆分编码字。
	if !isUTF8(charset) || base64.StdEncoding.EncodedLen(len(s)) <= maxContentLen {
		io.WriteString(w, s)
		w.Close()
		return
	}

	var currentLen, last, runeLen int
	for i := 0; i < len(s); i += runeLen {
		// 多字节字符不能在编码字之间拆分。
		// 参见RFC 2047第5.3节。
		_, runeLen = utf8.DecodeRuneInString(s[i:])

		if currentLen+runeLen <= maxBase64Len {
			currentLen += runeLen
		} else {
			io.WriteString(w, s[last:i])
			w.Close()
			e.splitWord(buf, charset)
			last = i
			currentLen = runeLen
		}
	}
	io.WriteString(w, s[last:])
	w.Close()
}

// qEncode使用Q编码并将其写入buf。必要时可拆分
// 编码字。
func (e WordEncoder) qEncode(buf *strings.Builder, charset, s string) {
	// 我们仅在字符集为UTF-8时拆分编码字。
	if !isUTF8(charset) {
		writeQString(buf, s)
		return
	}

	var currentLen, runeLen int
	for i := 0; i < len(s); i += runeLen {
		b := s[i]
		// 多字节字符不能在编码字之间拆分。
		// 参见RFC 2047第5.3节。
		var encLen int
		if b >= ' ' && b <= '~' && b != '=' && b != '?' && b != '_' {
			runeLen, encLen = 1, 1
		} else {
			_, runeLen = utf8.DecodeRuneInString(s[i:])
			encLen = 3 * runeLen
		}

		if currentLen+encLen > maxContentLen {
			e.splitWord(buf, charset)
			currentLen = 0
		}
		writeQString(buf, s[i:i+runeLen])
		currentLen += encLen
	}
}

// writeQString使用Q编码并将其写入buf。
func writeQString(buf *strings.Builder, s string) {
	for i := 0; i < len(s); i++ {
		switch b := s[i]; {
		case b == ' ':
			buf.WriteByte('_')
		case b >= '!' && b <= '~' && b != '=' && b != '?' && b != '_':
			buf.WriteByte(b)
		default:
			buf.WriteByte('=')
			buf.WriteByte(upperhex[b>>4])
			buf.WriteByte(upperhex[b&0x0f])
		}
	}
}

// openWord将编码单词的开头写入buf。
func (e WordEncoder) openWord(buf *strings.Builder, charset string) {
	buf.WriteString("=?")
	buf.WriteString(charset)
	buf.WriteByte('?')
	buf.WriteByte(byte(e))
	buf.WriteByte('?')
}

// closeWord将编码字的结尾写入buf。
func closeWord(buf *strings.Builder) {
	buf.WriteString("?=")
}

// splitWord关闭当前编码的字并打开一个新字。
func (e WordEncoder) splitWord(buf *strings.Builder, charset string) {
	closeWord(buf)
	buf.WriteByte(' ')
	e.openWord(buf, charset)
}

func isUTF8(charset string) bool {
	return strings.EqualFold(charset, "UTF-8")
}

const upperhex = "0123456789ABCDEF"

// 字解码器解码包含RFC 2047编码字的MIME头。
type WordDecoder struct {
	// CharsetReader，如果非nil，则定义一个函数来生成
	// charset转换读取器，将提供的
	// charset转换为UTF-8。
	// 字符集总是小写的。默认情况下处理utf-8、iso-8859-1和us ascii字符集
	// 。
	// CharsetReader的结果值之一必须为非零。
	CharsetReader func(charset string, input io.Reader) (io.Reader, error)
}

// 解码RFC 2047编码字。
func (d *WordDecoder) Decode(word string) (string, error) {
	// 请参阅https:
	// 我们的解码器是允许的，我们接受空编码文本。
	if len(word) < 8 || !strings.HasPrefix(word, "=?") || !strings.HasSuffix(word, "?=") || strings.Count(word, "?") != 4 {
		return "", errInvalidWord
	}
	word = word[2 : len(word)-2]

	// 拆分后的前两个字段
	split := strings.IndexByte(word, '?')

	// 将单词“UTF-8？q？ascii”拆分为“UTF-8”、“q”和“ascii”
	charset := word[:split]
	if len(charset) == 0 {
		return "", errInvalidWord
	}
	if len(word) < split+3 {
		return "", errInvalidWord
	}
	encoding := word[split+1]
	// 拆分后的字段只能是一个字节
	if word[split+2] != '?' {
		return "", errInvalidWord
	}
	text := word[split+3:]

	content, err := decode(encoding, text)
	if err != nil {
		return "", err
	}

	var buf strings.Builder

	if err := d.convert(&buf, charset, content); err != nil {
		return "", err
	}

	return buf.String(), nil
}

// 解码头解码给定字符串的所有编码字。当且仅当d的CharsetReader返回错误时，它返回一个
// 错误。
func (d *WordDecoder) DecodeHeader(header string) (string, error) {
	// 如果没有编码字，则在创建缓冲区之前返回。
	i := strings.Index(header, "=?")
	if i == -1 {
		return header, nil
	}

	var buf strings.Builder

	buf.WriteString(header[:i])
	header = header[i:]

	betweenWords := false
	for {
		start := strings.Index(header, "=?")
		if start == -1 {
			break
		}
		cur := start + len("=?")

		i := strings.Index(header[cur:], "?")
		if i == -1 {
			break
		}
		charset := header[cur : cur+i]
		cur += i + len("?")

		if len(header) < cur+len("Q??=") {
			break
		}
		encoding := header[cur]
		cur++

		if header[cur] != '?' {
			break
		}
		cur++

		j := strings.Index(header[cur:], "?=")
		if j == -1 {
			break
		}
		text := header[cur : cur+j]
		end := cur + j + len("?=")

		content, err := decode(encoding, text)
		if err != nil {
			betweenWords = false
			buf.WriteString(header[:start+2])
			header = header[start+2:]
			continue
		}

		// 在编码字之前写入字符。空格和换行符
		// 必须删除分隔两个编码字的字符。
		if start > 0 && (!betweenWords || hasNonWhitespace(header[:start])) {
			buf.WriteString(header[:start])
		}

		if err := d.convert(&buf, charset, content); err != nil {
			return "", err
		}

		header = header[end:]
		betweenWords = true
	}

	if len(header) > 0 {
		buf.WriteString(header)
	}

	return buf.String(), nil
}

func decode(encoding byte, text string) ([]byte, error) {
	switch encoding {
	case 'B', 'b':
		return base64.StdEncoding.DecodeString(text)
	case 'Q', 'q':
		return qDecode(text)
	default:
		return nil, errInvalidWord
	}
}

func (d *WordDecoder) convert(buf *strings.Builder, charset string, content []byte) error {
	switch {
	case strings.EqualFold("utf-8", charset):
		buf.Write(content)
	case strings.EqualFold("iso-8859-1", charset):
		for _, c := range content {
			buf.WriteRune(rune(c))
		}
	case strings.EqualFold("us-ascii", charset):
		for _, c := range content {
			if c >= utf8.RuneSelf {
				buf.WriteRune(unicode.ReplacementChar)
			} else {
				buf.WriteByte(c)
			}
		}
	default:
		if d.CharsetReader == nil {
			return fmt.Errorf("mime: unhandled charset %q", charset)
		}
		r, err := d.CharsetReader(strings.ToLower(charset), bytes.NewReader(content))
		if err != nil {
			return err
		}
		if _, err = io.Copy(buf, r); err != nil {
			return err
		}
	}
	return nil
}

// hasNonWhitespace报告s（假定为ASCII）是否至少包含一个字节的非空白。
func hasNonWhitespace(s string) bool {
	for _, b := range s {
		switch b {
		// 编码字只能用线性空格分隔，不包括垂直制表符（\v）。
		case ' ', '\t', '\n', '\r':
		default:
			return true
		}
	}
	return false
}

// qDecode对Q编码的字符串进行解码。
func qDecode(s string) ([]byte, error) {
	dec := make([]byte, len(s))
	n := 0
	for i := 0; i < len(s); i++ {
		switch c := s[i]; {
		case c == '_':
			dec[n] = ' '
		case c == '=':
			if i+2 >= len(s) {
				return nil, errInvalidWord
			}
			b, err := readHexByte(s[i+1], s[i+2])
			if err != nil {
				return nil, err
			}
			dec[n] = b
			i += 2
		case (c <= '~' && c >= ' ') || c == '\n' || c == '\r' || c == '\t':
			dec[n] = c
		default:
			return nil, errInvalidWord
		}
		n++
	}

	return dec[:n], nil
}

// readHexByte返回引用的可打印表示形式中的字节。
func readHexByte(a, b byte) (byte, error) {
	var hb, lb byte
	var err error
	if hb, err = fromHex(a); err != nil {
		return 0, err
	}
	if lb, err = fromHex(b); err != nil {
		return 0, err
	}
	return hb<<4 | lb, nil
}

func fromHex(b byte) (byte, error) {
	switch {
	case b >= '0' && b <= '9':
		return b - '0', nil
	case b >= 'A' && b <= 'F':
		return b - 'A' + 10, nil
	// 接受编码错误的字节。
	case b >= 'a' && b <= 'f':
		return b - 'a' + 10, nil
	}
	return 0, fmt.Errorf("mime: invalid hex byte %#02x", b)
}
